class_name UnitFighter
extends UnitBase

@onready var edge_ray_tester: RayCast2D = $ViewRoot/EdgeRayTester
@onready var leg_animationplayer: AnimationPlayer = $LegAnimationPlayer
@onready var body_animation_player: AnimationPlayer = $BodyAnimationPlayer

# attack
var attack_cooldown_timer: float = 0
@onready var melee_attack_area: Area2D = $ViewRoot/AttackArea
@onready var melee_collision: CollisionPolygon2D = $ViewRoot/AttackArea/CollisionPolygon2D
var melee_hit_targets: Array = []

# wander
var target_velocity: Vector2 = Vector2.ZERO


func pick_new_wander_target():
	if target_velocity.x == 0:
		target_velocity.x = speed if randf() < 0.5 else -speed
	else:
		target_velocity.x = speed if target_velocity.x < 0 else -speed
	
	velocity = target_velocity
	if velocity.x:
		set_face(sign(velocity.x))
	
	# 下一次切换目标时间
	timer = randf_range(2.0, change_dir_time)


func play_wander_animation():
	leg_animationplayer.play("walk")
	body_animation_player.play("idle")
	

func process_approach(delta: float):
	if not attack_target: 
		states.transition("Wander")
		return
	
	var target_pos = attack_target.global_position
	var dir = sign(target_pos.x - global_position.x) 
	var dx = target_pos.x - global_position.x
	
	if abs(dx) > unit_data.config.main_attack_range:
		velocity.x = dir * speed
		leg_animationplayer.play("walk")
	else:
		velocity.x = 0
		leg_animationplayer.play("idle")
	
	if not edge_ray_tester.is_colliding():
		velocity.x = 0
		leg_animationplayer.play("idle")
	
	move_and_slide()
	

func process_wander(delta: float):
	timer -= delta
	if timer <= 0.0 or not edge_ray_tester.is_colliding():
		pick_new_wander_target()

	velocity.x = lerp(velocity.x, target_velocity.x, 0.05) 
	move_and_slide()
		

func process_enter_attack(delta):
	if not attack_target: return
	
	var dist = abs(attack_target.global_position.x - global_position.x) 
	if dist > unit_data.config.main_attack_range: 
		attack_cooldown_timer = 0
		return
	
	attack_cooldown_timer -= delta
	
	if attack_cooldown_timer <= 0:
		attack_cooldown_timer = unit_data.config.main_attack_cooldown
		
		states.transition("Attack")
	
	
func process_melee_hit():
	if melee_collision.disabled: return
	
	var areas = melee_attack_area.get_overlapping_areas()
	if areas.size() <= 0: return
	
	for area in areas:
		var source = area.source
		if source not in melee_hit_targets:
			melee_hit_targets.append(source)
			source.take_hit(get_melee_hit_data(), self)
			
			if source is UnitBase:
				add_debuff_to_unit(source)


func add_debuff_to_unit(source: Node2D):
	var config: UnitFighterConfig = unit_data.config as UnitFighterConfig
	if config.flame_debuff_hit_rate:
		add_debuff_flame_to_unit(source)
	

func add_debuff_flame_to_unit(source: Node2D):
	var config: UnitFighterConfig = unit_data.config as UnitFighterConfig
	if not config.flame_debuff_hit_rate: return
	
	var unit = source as UnitBase
	
	var hit_rate = config.flame_debuff_hit_rate
	var is_hit = randf() < hit_rate
	if is_hit and not unit.get_buff_by_name("debuff_flame"):
		var buff_inst = unit.add_buff("debuff_flame", self)
		
		buff_inst.duration = config.flame_debuff_duration
		buff_inst.damage_per_second = config.flame_damage_per_second
		buff_inst.start_buff()


func get_melee_hit_data():
	var config = unit_data.config as UnitFighterConfig
	
	var critical_rate = config.main_critical_attack_rate
	var is_critical = randf() <= critical_rate 
	
	var hit_data = HitData.new()
	hit_data.is_critical = is_critical
	hit_data.attack = config.main_attack if not is_critical else config.main_attack * (1.0 + config.main_critical_attack_bonus)
	hit_data.knockback = config.main_attack_knockback
	
	return hit_data


func set_is_enemy(is_enemy: bool):
	super.set_is_enemy(is_enemy)
	
	if is_enemy:
		melee_attack_area.set_collision_mask_value(7, true)
		melee_attack_area.set_collision_mask_value(6, false)
	else:
		melee_attack_area.set_collision_mask_value(7, false)
		melee_attack_area.set_collision_mask_value(6, true)


func pause_animation():
	leg_animationplayer.pause()
	body_animation_player.pause()
	
	
func melee_attack_cam_shake():
	if not unit_data.config.main_attack_cam_shake: return
	
	var distance_2_cam_center = global_position.distance_to(level.camera.global_position)
	if distance_2_cam_center >  unit_data.config.main_attack_cam_shake_max_distance:
		return
	
	var dist_diff = abs(unit_data.config.main_attack_cam_shake_max_distance - distance_2_cam_center) 
	var rate = dist_diff * 1.0 / unit_data.config.main_attack_cam_shake_max_distance
	
	var target_cam_shake_intensity = rate * unit_data.config.main_attack_cam_shake_intensity
	level.camera.start_shake(target_cam_shake_intensity, unit_data.config.main_attack_cam_shake_duration)
	
